package com.jmb.api.web.core.parser;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.jmb.api.common.exception.ParseException;
import com.jmb.api.common.result.RelyType;
import com.jmb.api.common.utils.*;
import com.jmb.api.entity.Db;
import com.jmb.api.entity.DbConnection;
import com.jmb.api.entity.ExecuteInterfaceCaseParam;
import com.jmb.api.vo.Interface.Case.Execute.InterfaceCaseExecuteLogVO;
import com.jmb.api.vo.Interface.Case.Rely.InterfaceCaseRelyDataVO;
import com.jmb.api.vo.Interface.Pro.InterfaceProcessorVO;
import com.jmb.api.vo.rely.RelyDataVO;
import com.jmb.api.web.core.Node;
import com.jmb.api.web.env.Env;
import com.jmb.api.web.service.*;
import lombok.extern.slf4j.Slf4j;
import org.glassfish.jaxb.core.v2.TODO;
import org.jsoup.select.Evaluator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class Parser implements Node {
    @Autowired
    private InterfaceProcessorService interfaceProcessorService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private InterfaceCaseSuiteService ifSuiteService;
    @Autowired
    private InterfaceCaseRelyDataService ifCaseRelyDataService;
    @Autowired
    private InterfaceCaseService interfaceCaseService;
    @Autowired
    private InterfaceCaseExecuteLogService logService;
    @Autowired
    private RelyDataService relyDataService;
    @Autowired
    private DbService dbService;
    @Autowired
    private Env env;

    // 字符清洗数据
    /**
     * 字符清洗
     * @param text 待清洗数据
     * @param chainNo 调用链路跟踪 每次调用均会将自增日志编号写入缓存，再序列化
     * @param suiteId 测试套件编号，主要用于调用入口为测试套件时确定运行环境，否则应该传参null
     * @param isFailedRetry 判断该执行日志是否为失败重试，0是1否，主要用于测试报告统计断言情况
     * @param suiteLogDetailNo suiteLogNo仅记录重跑和真正运行的，suiteLogDetailNo会包括用例所依赖的case
     * @param globalHeaders 测试套件中的全局请求headers
     * @param globalParams 测试套件中的全局请求params
     * @param globalData 测试套件中的全局请求data
     * @param casePreNo 前置用例UUID编号
     * @return 清洗后的数据
     */
    public String parseDependency(String text, String chainNo, Long suiteId, Integer isFailedRetry, String suiteLogDetailNo,
                                  HashMap globalHeaders,HashMap globalParams,HashMap globalData,String casePreNo) throws SQLException, ClassNotFoundException {
        if (text ==null || text.trim().isEmpty()){
            return text;
        }
        // 解析处理器
        text = parseProcessor(text,suiteLogDetailNo,casePreNo,chainNo);
        log.info("-------------------------------------------开始字符ParseUtil串解析流程------------------------------");
        log.info("-------------------------------------------解析结果= {} ", text);
        // 获取运行的环境
        Integer runEnv = getRunEnv(suiteId);
        log.info("-------------------------------------------运行环境= {} ，0dev,1test,2stg,3prod,4debug ", runEnv);
        Pattern pattern = Pattern.compile(InterfaceConstants.DEPENDENCY_REGEX);
        Matcher matcher = pattern.matcher(text);
        while (matcher.find()){
            String findStr = matcher.group();
            String relyName = findStr.substring(2, findStr.length() - 1);
            String relyExpress = relyName; // 默认为依赖名称，带索引
            log.info("-------------------------------------------依赖名称(relyName)= {} ", relyName);
            // 进入数组下标取值
            if (Pattern.matches(InterfaceConstants.DEPENDENCY_REGEX_INDEX,relyName)){
                // 数组索引模式
                text = handleArrayIndexMode(text,findStr,relyName,relyExpress,chainNo,suiteId,isFailedRetry,suiteLogDetailNo,
                        globalHeaders,globalParams,globalData,casePreNo,runEnv);
            }
            // 进入预置方法，动态Sql模式
            else if (Pattern.matches(InterfaceConstants.DEPENDENCY_REGEX_PARAMS,relyName)) {
                text = handleMethodOrSqlMode(text,findStr,relyName,chainNo,suiteId,isFailedRetry,suiteLogDetailNo,
                        globalHeaders,globalParams,globalData,casePreNo,runEnv);
            }
            // 进入普通依赖数据模式
            else {
                text = handleNormalMode(text,findStr,relyName,relyExpress,chainNo,suiteId,isFailedRetry,suiteLogDetailNo,
                        globalHeaders,globalParams,globalData,casePreNo,runEnv);
                    }
            }


        return text;
    }

    // 处理数组索引模式
    private String handleArrayIndexMode(
            String text,
            String findStr,
            String relyName,
            String relyExpress,
            String chainNo,
            Long suiteId,
            Integer isFailedRetry,
            String suiteLogDetailNo,
            HashMap globalHeaders,
            HashMap globalParams,
            HashMap globalData,
            String casePreNo,
            Integer runEnv){
        long start = TimeUtil.now();
        log.info("-------------------------------------------开始数组索引模式解析流程------------------------------------");
        // 校验数组索引格式是否正确，通过判断[和]在字符串中首次与末次出现的位置是否一致
        if (relyName.indexOf("[")!=relyName.lastIndexOf("[") || relyName.indexOf("]")!=relyName.lastIndexOf("]")){
            log.info("-------------------------------------------数组索引格式错误，请检查格式是否正确");
            log.error("数组下标错误，relyName = {}",relyName);
            throw new ParseException("数组索引格式错误");
        }
        // 字符串中提取方括号 [] , 截取字符串
        String indexSrt = relyName.substring(relyName.indexOf("[") + 1, relyName.length() - 1);
        try {
            int index = Integer.parseInt(indexSrt);
            log.info("-------------------------------------------数组索引下标()= {} ", index);
            // 字符串中移除方括号 []及其包含的内容，比如data[$.status]，只取data
            relyName = relyName.substring(0,relyName.indexOf("["));
            log.info("-----------------------------去除下标后真实的数组索引名称()= {} ", relyName);
            InterfaceCaseRelyDataVO ifRelyData = ifCaseRelyDataService.findIfRelyDataByName(relyName);
            if (ifRelyData == null){
                log.error("依赖数据不存在，relyName = {}",relyName);
                log.error("-------------------------------------------依赖数据不存在，请检查依赖数据名称是否正确");
                String nf = String.format("依赖数据：relyName  ${%s} = ",relyName);
                throw new ParseException(nf);
            }
            // 获取用例编号
            Long caseId = ifRelyData.getRelyCaseId();
            log.info("---------------------------------------获取到的用例编号= {} ", caseId);
            ExecuteInterfaceCaseParam caseParam = new ExecuteInterfaceCaseParam(
                    caseId,"系统调度",null,chainNo,suiteId,isFailedRetry,
                    suiteLogDetailNo,globalHeaders,globalParams,globalData
                    ,4,casePreNo,false);
            Long executeLogId = interfaceCaseService.executeInterfaceCase(caseParam);
            log.info("------------------------------用例执行完毕，用例编号= {} 执行日志编号= {}",caseId, executeLogId);
            if (logService.findExecute(executeLogId).getStatus() != 0){
                log.error("-------------------------------------------用例执行失败，请检查用例是否正确");
                String format = String.format("用例 [%s]执行失败 ，请检查用例是否正确，请检查编号正确性 [%s]", caseId, executeLogId);
                throw new ParseException(format);
            }

            InterfaceCaseExecuteLogVO logServiceExecute = logService.findExecute(executeLogId);
            String responseBody = logServiceExecute.getResponseBody();
            String responseHeaders = logServiceExecute.getResponseHeaders();
            log.info("--------------------------------------依赖用例获取到的响应数据= {} ", responseBody);
            log.info("--------------------------------------依赖用例获取到的响应头= {} ", responseHeaders);
            // 获取type
            Integer contentType = ifRelyData.getContentType();
            // 获取表达式
            String expression = ifRelyData.getExtractExpression();
            log.info("-------------------------------------------获取到的表达式= {} ", expression);
            // 进入type判断
            try {
                // json模式
                if (contentType == 0){
                    ArrayList jsonPathArray = JSONObject.parseObject(ParseUtil.parseJson(responseBody, expression), ArrayList.class);
                    if (jsonPathArray.isEmpty()){
                        log.error("-------------------------------------------依赖数据解析错误，请检查依赖数据名称是否正确");
                        String nf = String.format("依赖数据：relyName [%s] json-路径提取内容为空 caseId是  =  [%s]" +
                                "日志log id是 [%s]  提取表达式是 [%s]",relyName,caseId,executeLogId,expression);
                        throw new ParseException(nf);
                    }
                    try {
                        // 获取值
                        String value = jsonPathArray.get(index).toString();
                        // 写入redis
                        redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_JSON,executeLogId,relyExpress,value,
                                TimeUtil.now()- start,expression));
                        text = text.replace(findStr,value);
                        log.info("------------------------------------jsonPath提取值并替换后的结果 {}",text);
                    }catch (Exception e){
                        String format = String.format("jsonPath提取值错误，请检查依赖数据名称是否正确，" +
                                "依赖数据名称是 [%s] index数值越界[%s]", relyName, index);
                        log.error(format);
                        throw new ParseException(format);
                    }
                }
                // html模式
                else if (contentType == 1){
                    ArrayList xpathArray = JSONObject.parseObject(ParseUtil.parseXml(responseBody, expression), ArrayList.class);
                    if (xpathArray.isEmpty()){
                        String nf = String.format("依赖数据：relyName [%s] xpath-路径提取内容为空 caseId是  =  [%s]" +
                                "日志log id是 [%s]  提取表达式是 [%s]",relyName,caseId,executeLogId,expression);

                        log.error(nf);
                        throw new ParseException(nf);
                    }
                    try {
                        String value = xpathArray.get(index).toString();
                        redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_HTML,executeLogId,relyExpress,value,
                                TimeUtil.now()- start,expression));
                        text = text.replace(findStr,value);
                        log.info("------------------------------------xpath提取值并替换后的结果 {}",text);
                    }catch (Exception e){
                        String format = String.format("xpath提取值错误，请检查依赖数据名称是否正确，" +
                                "依赖数据名称是 [%s] index数值越界[%s]", relyName, index);
                        log.error(format);
                        throw new ParseException(format);
                    }
                }
                // headers模式
                else if (contentType == 2){
                    JSONArray headerArray = (JSONArray) JSONObject.parseObject(responseHeaders, HashMap.class).get(expression);
                    if (headerArray ==  null ||headerArray.isEmpty()){
                        String nf = String.format("依赖数据：relyName [%s] headers-路径提取内容为空 caseId是  =  [%s]" +
                                "日志log id是 [%s]  提取表达式是 [%s]",relyName,caseId,executeLogId,expression);
                        log.error(nf);
                        throw new ParseException(nf);
                    }
                    try {
                        String value = headerArray.get(index).toString();
                        redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_HEADER,executeLogId,relyExpress,value,
                                TimeUtil.now()- start,expression));
                        text = text.replace(findStr,value);
                        log.info("------------------------------------headers提取值并替换后的结果 {}",text);
                    }catch (Exception e){
                        String format = String.format("headers提取值错误，请检查依赖数据名称是否正确，" +
                                "依赖数据名称是 [%s] index数值越界[%s]", relyName, index);
                        log.error(format);
                        throw new ParseException(format);
                    }

                }
                else {
                    log.error("-------------------------------------------依赖数据解析错误，请检查依赖数据名称是否正确,不支持提取类型");
                    throw new ParseException("不支持提取类型");
                }

            }
            catch (ParseException e){
                log.error("-------------------------------------------依赖数据解析错误，不支持提取类型");
                throw new ParseException(e.getMessage()+"不支持提取类型");
            }

        }catch (Exception e){
            log.error("数组索引下标错误 "+e.getMessage());
            throw new ParseException("数组索引下标错误"+e.getMessage());
        }
        return text;
    }



    /**
     * 处理方法或SQL模式
     */
    private String handleMethodOrSqlMode(String text,String findStr,String relyName,String chainNo,Long suiteId,
                                         Integer isFailedRetry,String suiteLogDetailNo,
                                         HashMap globalHeaders,
                                         HashMap globalParams,
                                         HashMap globalData,
                                         String casePreNo,
                                         Integer runEnv) throws ClassNotFoundException, SQLException {
        long start = TimeUtil.now();
        log.info("-------------------------------------------开始预置方法或SQL模式解析流程------------------------------------");
        if (relyName.indexOf("(") != relyName.lastIndexOf("(") ||
                relyName.indexOf(")") != relyName.lastIndexOf(")")) {
            String nf = String.format("依赖数据：relyName [%s] 方法或SQL模式参数错误，请检查依赖数据名称是否正确，", relyName);
            log.error(nf);
            throw new ParseException(nf);
        }
        String methodName = relyName.substring(0, relyName.indexOf("("));
        log.info("--------------------------预置方法名称/动态SQL依赖名称={}",methodName);
        String[] params = relyName.substring(relyName.indexOf("(") + 1, relyName.length() - 1)
                .replaceAll("\\s+", ",").split( ",");
        RelyDataVO relyDataVO = relyDataService.findRelyDataByName(relyName);
        if (relyDataVO == null){
            String nf = String.format("依赖数据：relyName [%s] 方法或SQL模式依赖数据不存在，请检查依赖数据名称是否正确，", relyName);
            log.error(nf);
            throw new ParseException(nf);
        }
        // 获取type
        Integer type = relyDataVO.getType();
        if (type == 1){
            // 反射方法
            log.info("-------------------------------------------进入预置方法模式------------------------------------");
            handleInvokeMethod(text,findStr,methodName,params,relyDataVO,chainNo,runEnv,start);
        } else if (type >= 2 && type <= 6) { //sql 2sql-select 3sql-insert 4sql-update 5sql-delete 6sql-script
            log.info("--------------------------------------进入动态SQL模式");
            text = handleSqlExecution(text, findStr, params, relyDataVO, chainNo, suiteId, isFailedRetry, suiteLogDetailNo,
                    globalHeaders, globalParams, globalData, casePreNo, runEnv, start);
        }
        return text;

    }

    /**
     * 处理反射方法调用
     */
    private String handleInvokeMethod(String text,String findStr,String methodName,String [] params,
                                      RelyDataVO relyDataVO,String chainNo,Integer runEnv,Long start) throws ClassNotFoundException {
        log.info("-------------------------------------------进入反射方法处理模式------------------------------------");
        // 获取方法
        Method method;
        // 获取参数
        String methodReturnValue;
        // 获取类
        Class<?> clazz;
        // 获取构造方法
        Constructor<?> constructor;

        try {
            clazz = Class.forName(InterfaceConstants.INVOKE_CENTER_CLASS);
            constructor = clazz.getConstructor(Integer.class);
        }catch (Exception e){
            log.error("-------------------------------------------反射方法调用错误，请检查依赖数据名称是否正确,请检查依赖数据名称是否正确");
            throw new ParseException("反射方法调用错误，请检查依赖数据名称是否正确，找不到InvokeCenter函数");
        }
        if (params.length == 1 && "".equals(params[0])){
            params = new String[0];
        }
        try {
            // 尝试固定长度参数
            clazz = Class.forName(InterfaceConstants.INVOKE_CENTER_CLASS);
            constructor = clazz.getConstructor(Integer.class);
            Class[] paramsList = new Class[params.length];
            for (int i = 0; i < params.length; i++) {
                paramsList[i] = String.class;
                params [i] = params[i].substring(1,params[i].length()-1);
            }
            log.info("------------------进入固定参数模式----方法名称={}-----方法参数={}-----------",methodName,paramsList);
            method = clazz.getMethod(methodName,paramsList);
            methodReturnValue = (String) method.invoke(constructor.newInstance(runEnv),params);
            log.info("------------------方法调用成功，耗时{}毫秒，返回值={}------------------",TimeUtil.now()- start,methodReturnValue);
            log.info("------固定长度参数，预置方法替换后的结果={}------------",text);
        }
        catch (Exception e) {
            log.error(e.getMessage());
            // 尝试参数长度可变参数
            try {
                log.info("---------固定长度参数异常---进入参数长度可变模式----方法名称={}-----方法参数={}-----------",
                        methodName, Arrays.toString( params));
                method = clazz.getMethod(methodName,String[].class);
                methodReturnValue = (String) method.invoke(constructor.newInstance(runEnv),params);
                log.info("---------方法调用成功，耗时{}毫秒，返回值={}------------------",TimeUtil.now()- start,methodReturnValue);
                log.info("固定长度参数异常，尝试可变长度参数，预置方法执行并替换后的结果={}", text);
            } catch (Exception ex) {
                String nf = String.format("方法调用错误，请检查依赖数据名称是否正确,请检查依赖数据名称是否正确 [%s] ,[%s]",methodName,Arrays.toString( params));
                log.error(nf);
                throw new ParseException(nf);
            }
        }
        redisUtil.stackPush(chainNo,chainNode(RelyType.INVOKE,relyDataVO.getId(),methodName,methodReturnValue,
                TimeUtil.now()-start,null));
        text=  text.replace(findStr,methodReturnValue);
        return text;
    }

    /**
     * 处理SQL执行
     */
    private String handleSqlExecution(String text,String findStr,String []  params,RelyDataVO relyDataVO,String chainNo,
                                      Long suiteId,Integer isFailedRetry,String suiteLogDetailNo,HashMap globalHeaders,
                                      HashMap globalParams,HashMap globalData,String casePreNo,Integer runEnv,Long start) throws SQLException, ClassNotFoundException {
        log.info("-------------------------------------------进入SQL执行模式------------------------------------");
        if (params.length == 1 && "".equals(params[0])){
            params = null;
        }else {
            for (int i = 0; i <params.length ; i++) {
                // 去除首尾引号
                params[i] = params[i].substring(1,params[i].length()-1);

            }
        }
        Integer datasourceId = relyDataVO.getDatasourceId();
        if (datasourceId == null){
            String nf = String.format("数据源不存在，请检查依赖数据源ID，[%s]", datasourceId);
            log.error(nf);
            throw new ParseException(nf);
        }
        Db db = dbService.getById(datasourceId);
        Integer status = db.getStatus();
        if (status == 1){
            log.warn("该数据源已禁用，dbName= {}",db.getName());
            String nf = String.format("该数据源已禁用，请检查依赖数据源ID，[%s]", datasourceId);
            log.error(nf);
            throw new ParseException(nf);
        }
        DbConnection datasource = env.datasource(db, runEnv);
        String url = datasource.getUrl();
        String username = datasource.getUsername();
        String password = datasource.getPassword();
        // 支持动态sql
        String sql = relyDataVO.getValue();
        if (relyDataVO.getValue() !=null){
            log.info("---------------------------开始动态SQL解析-原始Sql={}------------------------", sql);
            sql = parseDependency(sql,chainNo,suiteId,isFailedRetry,suiteLogDetailNo,globalHeaders,globalParams,globalData,casePreNo);
            log.info("---------------------------动态SQL解析完成-解析后的Sql={}------------------------", sql);
        }
        log.info("---------SQL执行参数-SQL={}----params={}-------------------", sql,params);
        String sqlResult = executeSqlBasedOnType(relyDataVO, url, username, password, sql, params, chainNo, start);
        text = text.replace(findStr,sqlResult);
        return text;
    }
    /**
     * 根据类型执行SQL
     */
    public String executeSqlBasedOnType(RelyDataVO relyDataVO,String url,String username,String password,String sql,String [] params,
                                        String chainNo,Long start) throws SQLException {
        String sqlResult;
        Integer type = relyDataVO.getType();
        if (type == 2){//查询
            if (relyDataVO.getAnalysisRely() == 0){
                 sqlResult = JdbcUtil.selectFirst(url, username, password, sql, params);
            }else {
                sqlResult = JdbcUtil.selectFirst(url, username, password, relyDataVO.getValue(), params);
            }
            redisUtil.stackPush(chainNo,chainNode(RelyType.SQL_SELECT,relyDataVO.getId(),relyDataVO.getName(),sqlResult,
                    TimeUtil.now()-start,null));
        }
        else if (type == 3){ //新增
            if (relyDataVO.getEnableReturn() == 0){
                sqlResult = String.valueOf(JdbcUtil.insert(url, username, password, sql, params));
            }else {
                JdbcUtil.insert(url, username, password, relyDataVO.getValue(), params);
                sqlResult = "";
            }
            redisUtil.stackPush(chainNo,chainNode(RelyType.SQL_INSERT,relyDataVO.getId(),relyDataVO.getName(),sqlResult,
                    TimeUtil.now()-start,null));
        }
        else if (type == 4){ // 修改
            if (relyDataVO.getAnalysisRely() == 0){
                sqlResult = JdbcUtil.update(url, username, password, sql, params);
            }else {
                sqlResult = JdbcUtil.update(url, username, password, relyDataVO.getValue(), params);
            }
            redisUtil.stackPush(chainNo,chainNode(RelyType.SQL_UPDATE,relyDataVO.getId(),relyDataVO.getName(),sqlResult,
                    TimeUtil.now()-start,null));
        }
        else if (type == 5){ //删除
            if (relyDataVO.getAnalysisRely() == 0){
                sqlResult = JdbcUtil.delete(url, username, password, sql, params);
            }else {
                sqlResult = JdbcUtil.delete(url, username, password, relyDataVO.getValue(), params);
            }
            redisUtil.stackPush(chainNo,chainNode(RelyType.SQL_DELETE,relyDataVO.getId(),relyDataVO.getName(),sqlResult,
                    TimeUtil.now()-start,null));
        }
        else { //脚本
            if (relyDataVO.getAnalysisRely() == 0){
                sqlResult = JdbcUtil.script(sql,url, username, password, true);
            }else {
                sqlResult = JdbcUtil.script(relyDataVO.getValue(),url, username, password, true);
            }
            redisUtil.stackPush(chainNo,chainNode(RelyType.SQL_SCRIPT,relyDataVO.getId(),relyDataVO.getName(),sqlResult,
                    TimeUtil.now()-start,null));
        }
        return sqlResult;
    }

    /**
     * 处理普通模式
     */
    private String handleNormalMode(String text, String findStr, String relyName, String relyExpress, String chainNo, Long suiteId,
                                    Integer isFailedRetry, String suiteLogDetailNo,HashMap globalHeaders,HashMap globalParams,HashMap globalData,
                                    String casePreNo,Integer runEnv) throws SQLException, ClassNotFoundException {
        long start = TimeUtil.now();
        log.info("-------------------------------------------进入普通依赖数据模式------------------------------------");
        InterfaceCaseRelyDataVO interfaceCaseRelyDataVO = ifCaseRelyDataService.findIfRelyDataByName(relyName);
        if (interfaceCaseRelyDataVO == null){
            RelyDataVO relyDataVO = relyDataService.findRelyDataByName(relyName);
            if (relyDataVO == null){
                String nf = String.format("依赖数据不存在，请检查依赖数据名称，[%s]", relyName);
                log.error(nf);
                throw new ParseException(nf);
            }else {
               text =  handleRelyData(text, findStr, relyName, relyDataVO, chainNo, suiteId, isFailedRetry, suiteLogDetailNo,
                        globalHeaders, globalParams, globalData, casePreNo, runEnv, start);
            }
        }else {
            text = handleInterfaceCaseRelyData(text, findStr, relyName, interfaceCaseRelyDataVO, chainNo, suiteId, isFailedRetry, suiteLogDetailNo,
                    globalHeaders, globalParams, globalData, casePreNo, start);
        }
        return text;
    }

    /**
     * 处理依赖数据
     */
    private String handleRelyData(String text, String findStr, String relyName, RelyDataVO relyDataVO,String chainNo,Long suiteId,
                                  Integer isFailedRetry, String suiteLogDetailNo,HashMap globalHeaders,HashMap globalParams,HashMap globalData,
                                  String casePreNo,Integer runEnv,Long  start) throws SQLException, ClassNotFoundException {
        log.info("-------------------------------------------进入处理依赖数据模式------------------------------------");
        Integer type = relyDataVO.getType();
        if (type == 0){
            String value = relyDataVO.getValue();
            text = text.replace(findStr,value);
            redisUtil.stackPush(chainNo,chainNode(RelyType.CONST,relyDataVO.getId(),relyName,value, TimeUtil.now()-start,null));
        }else if (type >= 2 && type <= 6){
            Integer datasourceId = relyDataVO.getDatasourceId();
            if (datasourceId == null){
                String nf = String.format("数据源不存在，请检查数据源名称 数据源id是，[%s]",datasourceId);
                log.error(nf);
                throw new ParseException(nf);
            }
            Db dbvo = dbService.getById(datasourceId);
            Integer status = dbvo.getStatus();
            if (status == 1){
                String nf = String.format("数据源访问被拒绝，id 是[%s]", dbvo.getName());
                log.error(nf);
                throw new ParseException(nf);
            }
            DbConnection datasource = env.datasource(dbvo, runEnv);
            String url = datasource.getUrl();
            String username = datasource.getUsername();
            String password = dbvo.getPassword();
            String sql = relyDataVO.getValue();
            if (relyDataVO.getValue() != null){
                log.info("开始解析Sql，解析前的Sql= {}", sql);
                sql = parseDependency(sql, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
                log.info("开始解析Sql，解析后的Sql= {}", sql);
            }
            log.info("SQL执行参数，SQL={}", sql);
            String sqlResult = executeSqlBasedOnType(relyDataVO,url,username,password,sql,null,chainNo,start);
            text = text.replace(findStr,sqlResult);
        }else {
            String nf = String.format("不支持的依赖数据类型，请检查依赖数据类型，[%s]", relyName);
            log.error(nf);
            throw new ParseException(nf);
        }
        return text;
    }

    /**
     * 处理接口用例依赖数据
     */
    private String handleInterfaceCaseRelyData(String text, String findStr, String relyName, InterfaceCaseRelyDataVO interfaceCaseRelyDataVO,
                                               String chainNo, Long suiteId, Integer isFailedRetry, String suiteLogDetailNo,
                                               HashMap globalHeaders, HashMap globalParams, HashMap globalData,
                                               String casePreNo, Long start){
        log.info("-------------------------------------------进入处理接口用例依赖数据模式------------------------------------");
        Long caseId = interfaceCaseRelyDataVO.getRelyCaseId();
        ExecuteInterfaceCaseParam caseParam = new ExecuteInterfaceCaseParam(caseId, "系统调度", null, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders,
                globalParams, globalData, 4, casePreNo, false);
        Long executeLogId = interfaceCaseService.executeInterfaceCase(caseParam);
        if (logService.findExecute(executeLogId).getStatus() !=0){
            String nf = String.format("dependency related case [%s] execution failed or error, " +
                    "the execution log id was [%s]", caseId, executeLogId);
            log.error(nf);
            throw new ParseException(nf);
        }
        InterfaceCaseExecuteLogVO interfaceCaseExecuteLogVO = logService.findExecute(executeLogId);
        String responseBody = interfaceCaseExecuteLogVO.getResponseBody();
        String responseHeaders = interfaceCaseExecuteLogVO.getResponseHeaders();
        log.info("依赖用例 responseBody: {}", responseBody);
        log.info("依赖用例 responseHeaders: {}", responseHeaders);
        Integer contentType = interfaceCaseRelyDataVO.getContentType();
        String expression = interfaceCaseRelyDataVO.getExtractExpression();
        try {
            if (contentType == 0){ // json
                ArrayList jsonPathArray = JSONObject.parseObject(ParseUtil.parseJson(responseBody, expression), ArrayList.class);
                if (jsonPathArray.isEmpty()){
                    String nf = String.format("依赖用例[%s] json-路径提取内容为空 用例ID为[%s]，执行日志ID为[%s]，表达为[%s]", relyName,caseId,executeLogId,expression);
                    log.error(nf);
                    throw new ParseException(nf);
                }
                if (jsonPathArray.size() == 1) {
                    Object o = jsonPathArray.get(0);
                    redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_JSON,executeLogId,relyName,o.toString(), TimeUtil.now()-start,expression));
                    text = text.replace(findStr,o.toString());
                }else {
                    redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_JSON,executeLogId,relyName, JSON.toJSONString(jsonPathArray), TimeUtil.now()-start,expression));
                    text = text.replace(findStr,JSON.toJSONString(jsonPathArray));
                }
                log.info("依赖用例 json-路径提取结果为：{}", text);
            }else if (contentType == 1){ // html
                ArrayList xpathArray = JSONObject.parseObject(ParseUtil.parseXml(responseBody, expression), ArrayList.class);
                if (xpathArray.isEmpty()){
                    String nf = String.format("依赖用例[%s] html-路径提取内容为空 用例ID为[%s]，执行日志ID为[%s]，表达为[%s]", relyName,caseId,executeLogId,expression);
                    log.error(nf);
                    throw new ParseException(nf);
                }
                if (xpathArray.size() == 1) {
                    Object o = xpathArray.get(0);
                    redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_HTML,executeLogId,relyName,o.toString(), TimeUtil.now()-start,expression));
                    text = text.replace(findStr,o.toString());
                }else {
                    redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_HTML,executeLogId,relyName, JSON.toJSONString(xpathArray), TimeUtil.now()-start,expression));
                    text = text.replace(findStr,JSON.toJSONString(xpathArray));
                }
                log.info("依赖用例 html-路径提取结果为：{}", text);
            }
            else if (contentType == 2){ // header
                ArrayList headerArray = JSONObject.parseObject(ParseUtil.parseJson(responseHeaders, expression), ArrayList.class);
                if (headerArray == null || headerArray.isEmpty()){
                    String nf = String.format("依赖用例[%s] header-路径提取内容为空 用例ID为[%s]，执行日志ID为[%s]，表达为[%s]", relyName,caseId,executeLogId,expression);
                    log.error(nf);
                    throw new ParseException(nf);
                }else {
                    if (headerArray.size() == 1) {
                    Object o = headerArray.get(0);
                    redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_HEADER,executeLogId,relyName,o.toString(), TimeUtil.now()-start,expression));
                    text = text.replace(findStr,o.toString());
                }else {
                    redisUtil.stackPush(chainNo,chainNode(RelyType.INTERFACE_HEADER,executeLogId,relyName, JSON.toJSONString(headerArray), TimeUtil.now()-start,expression));
                    text = text.replace(findStr,JSON.toJSONString(headerArray));
                }
                    log.info("依赖用例 header-路径提取结果为：{}", text);
            }
        }else {
                throw new ParseException("不支持提取类型");
            }
        }catch (Exception e){
            String nf = String.format("依赖用例提取表达式解析异常，请检查表达式，[%s]", expression);
            log.error(nf);
            throw new ParseException(nf);
        }
        return text;

    }


    /**
     * 获取运行环境
     * @param suiteId 测试套件ID
     * @return 运行环境
     */
    private Integer getRunEnv(Long suiteId)  {
        if (suiteId == null) {
            // 如果id是为空，我们直接默认是debug环境，所以这个写了一个4
            return 4;
        } else {
            return ifSuiteService.findInterfaceCaseSuiteById(suiteId).getRunDev();
        }
    }

    // 解析处理器
    /**
     * 解析处理器
     * @param text 原文
     * @param suiteLogDetailNo 非空时使用测试套件域，否则使用临时域
     * @param casePreNo 组合用例的前置用例缓存redis hash key
     * @param chainNo 链路跟踪redis key
     * @return 解析后的字符串
     */
    public String parseProcessor(String text, String suiteLogDetailNo, String casePreNo,String chainNo){
        log.info("-------------------------------------------开始处理器提取解析流程------------------------------------");
        log.info("-------------------------------------------待解析字符串原文= {} ", text);
        if (text ==null || text.trim().isEmpty()){
            return text;
        }
        Pattern pattern = Pattern.compile(InterfaceConstants.DEPENDENCY_REGEX);
        Matcher matcher = pattern.matcher(text);
        while (matcher.find()){
            long start = TimeUtil.now();
            String findStr = matcher.group();
            String postProcessorName = findStr.substring(2, findStr.length() - 1);
            InterfaceProcessorVO postProcessor = interfaceProcessorService.findInterfaceProcessorByName(postProcessorName);
            log.info("-------------------------------------------处理器名称= {} ", postProcessorName);
            Object redisResult;
            if (casePreNo == null){
                if (suiteLogDetailNo == null){
                    log.info("--suiteLogDetailNo == null && casePreNo==null-----------非组合用例，使用临时域-----------------");
                    // 临时域,TEMP_PROCESSOR_NO是定义的缓存key，处理器的名称
                    redisResult = redisUtil.hashGet(NoUtil.TEMP_POST_PROCESSOR_NO, postProcessorName);
                }else {
                    log.info("--suiteLogDetailNo == null && casePreNo!=null-----------组合用例,使用测试套件例域---------------");
                    redisResult = redisUtil.hashGet(suiteLogDetailNo, postProcessorName);
                }
            }else {
                log.info("--casePreNo != null---------使用前置用例域---------------");
                redisResult = redisUtil.hashGet(casePreNo, postProcessorName);
            }
            if (redisResult == null){
                log.info("----------------------处理器名称= {} 未找到----------------", postProcessorName);
                String format = String.format("----------------处理器名称= [%s] 未找到----------------", postProcessorName);
                log.error( format);
                throw new ParseException(format);
            }
            log.info("--------------------------------------处理器名称= {} 缓存结果= {} ", postProcessorName, redisResult);
            // 缓存结果
            String redisUtilString = redisUtil.toString();
            // 链路跟踪
            redisUtil.stackPush(chainNo,
                    chainNode(RelyType.READ_PROCESSOR,null,postProcessorName,redisUtilString,
                            TimeUtil.now()-start,null));
            //
            text = text.replace(findStr, redisUtilString);
            // todo 缺少写处理器日志表，待完善
        }
        log.info("-------------------------------------------解析结果= {} ", text);
        log.info("-------------------------------------------结束处理器提取解析流程----------------------------------");
        return text;
    }


    // 提取文本名称
    /**
     * 从给定文本中提取依赖项名称
     *
     * @param text 需要解析的文本内容
     * @return 包含提取出的依赖项名称的ArrayList列表
     */
    public ArrayList<String> extractTextName(String text) {
        ArrayList<String> testList = new ArrayList<>();
        // 检查文本是否非空且不为空白字符串
        if (text !=null && text.trim().isEmpty()){
            // 去除处理器，否则若依赖中包含处理器解析错误
            text = text.replaceAll(InterfaceConstants.PROCESSOR_REGEX, "");
            Matcher matcher = Pattern.compile(InterfaceConstants.DEPENDENCY_REGEX).matcher(text);
            // 遍历所有匹配的依赖项表达式
            while (matcher.find()) {
                String finds = matcher.group();
                String dependencyExpression = finds.substring(2, finds.length() - 1);
                // 根据不同的依赖项格式进行分类处理
                if (Pattern.matches(InterfaceConstants.DEPENDENCY_REGEX_INDEX, dependencyExpression)){
                    // 数组下标 带[]
                    String dependencyName = dependencyExpression.substring(0, dependencyExpression.indexOf("["));
                    testList.add(dependencyName);
                }else if (Pattern.matches(InterfaceConstants.DEPENDENCY_REGEX_PARAMS, dependencyExpression)){
                    // 方法或者sql 带()
                    String dependencyName = dependencyExpression.substring(0, dependencyExpression.indexOf("("));
                    testList.add(dependencyName);
                }
                else {
                    // 普通模式
                    testList.add(dependencyExpression);
                }
            }
        }
        return testList;
    }

}
